//
//  YHBLETool.m
//  BLE4.0Example
//
//  Created by zxy on 2017/9/27.
//  Copyright © 2017年 saint. All rights reserved.
//

#import "YHBLECentralManager.h"
#import "YHPeripheral.h"
#import <SVProgressHUD/SVProgressHUD.h>
#define kPeripheralName @"CGB_TEST" //外围设备名称
#define kServiceUUID @"6E400001-B5A3-F393-E0A9-E50E24DCCA9E" //服务的UUID
#define kCharacteristicUUID @"6A3E4B28-522D-4B3B-82A9-D5E2004534FC" //特征的

#define kCharacteristicREADUUID  @"6E400003-B5A3-F393-E0A9-E50E24DCCA9E"//收

#define kCharacteristicWRITEUUID @"6E400002-B5A3-F393-E0A9-E50E24DCCA9E"  //写




NSString *const YH_BLE_STATE_CHANGE = @"YH_BLE_STATE_CHANGE";
NSString *const YH_BLE_CBPeripheral_found = @"YH_BLE_CBPeripheral_found";
NSString *const YH_BLE_SCANFSTATE = @"YH_BLE_SCANFSTATE";
NSString *const YH_BLE_MESSAGE_COMMING = @"YH_BLE_MESSAGE_COMMING";


@implementation YHBLECentralManager

+(instancetype)shared{
    static dispatch_once_t onceToken;
    static YHBLECentralManager *shareInstance = nil;
    dispatch_once(&onceToken, ^{
        shareInstance = [[YHBLECentralManager alloc]init];
    });
    return shareInstance;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.centerManager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
        self.centerManager.delegate = self;
    }
    return self;
}


#pragma mark - 开放函数

- (void)startScan{
    if (self.centerManager.state != CBManagerStatePoweredOn) {
        UIAlertController *tipAlert = [UIAlertController alertControllerWithTitle:@"提示" message:@"请先打开蓝牙" preferredStyle:UIAlertControllerStyleAlert];
        [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:tipAlert animated:YES completion:nil];
        return;
        
    }
    [self.centerManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:[NSNumber numberWithInt:1],CBCentralManagerOptionShowPowerAlertKey:[NSNumber numberWithInt:1]}];
    [[NSNotificationCenter defaultCenter] postNotificationName:YH_BLE_SCANFSTATE object:@(YES)];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:YH_BLE_SCANFSTATE object:@(NO)];
        [self stopScan];
    });
    
}

- (void)startScanWith:(NSTimeInterval)time{
    [self stopScan];
    [self startScan];
    self.scanfTimer = [NSTimer scheduledTimerWithTimeInterval:time repeats:YES block:^(NSTimer * _Nonnull timer) {
        [self startScan];
    }];
}

- (void)stopScan{
    [self.centerManager stopScan];
}


-(void)connectTo:(CBPeripheral*)peripheral{
    if (self.centerManager.isScanning) {
        [self.centerManager stopScan];
    }
    if (peripheral.state != CBPeripheralStateDisconnected) {
        
    }else{
        [self.centerManager connectPeripheral:peripheral options:nil];
    }
}

- (void)disConnect{
    [self.centerManager cancelPeripheralConnection:self.currentConnectedPeripheral];
}


#pragma mark - GET SET

- (NSMutableArray<CBPeripheral *> *)peripherals{
    if (!_peripherals) {
        _peripherals = [[NSMutableArray alloc]init];
    }
    return _peripherals;
}

- (NSMutableData *)reciveBuffer{
    if (!_reciveBuffer) {
        _reciveBuffer = [[NSMutableData alloc]init];
    }
    return _reciveBuffer;
}

#pragma mark - 私有方法

- (void)addNewPeripheral{
    
}

- (void)sendMessage:(NSData *)data{
    
    if (self.currentConnectedPeripheral.state != CBPeripheralStateConnected) {
        [SVProgressHUD showErrorWithStatus:@"设备未连接"];
        return;
    }
    CBCharacteristic *write = nil;
    for (CBService *sevice in self.currentConnectedPeripheral.services) {
        
        if ([sevice.UUID.UUIDString isEqualToString:kServiceUUID]) {
            
            for (CBCharacteristic *characteristic in sevice.characteristics) {
                
                if ([characteristic.UUID.UUIDString isEqualToString:kCharacteristicREADUUID]) {
                    [self.currentConnectedPeripheral setNotifyValue:YES forCharacteristic:characteristic];
                }
                if ([characteristic.UUID.UUIDString isEqualToString:kCharacteristicWRITEUUID]) {
                    write = characteristic;
                }
            }
        }
        
    }
    
    
    
    
    if (!write) {
        NSLog(@"没有写权限");
        return;
    }
    
    
    
    int a = (int)(data.length/20);
    int b = (int)(data.length%20);
    
    
    for (int offset = 0; offset < a; offset ++) {
        NSData *subData = [data subdataWithRange:NSMakeRange(offset*20, 20)];
        [self.currentConnectedPeripheral writeValue:subData forCharacteristic:write type:CBCharacteristicWriteWithResponse];
    }
    NSData *subData = [data subdataWithRange:NSMakeRange(a*20, b)];
    [self.currentConnectedPeripheral writeValue:subData forCharacteristic:write type:CBCharacteristicWriteWithResponse];
   
}




#pragma mark - CBCentralManagerDelegate
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
    switch (central.state) {
        case CBManagerStatePoweredOn:
            NSLog(@"BLE已打开.");
            break;
        default:
            NSLog(@"蓝牙状态异常，请检查蓝牙是否打开");
            break;
    }
}


/*!
 *  @method centralManager:willRestoreState:
 *
 *  @param central      The central manager providing this information.
 *  @param dict			A dictionary containing information about <i>central</i> that was preserved by the system at the time the app was terminated.
 *
 *  @discussion			For apps that opt-in to state preservation and restoration, this is the first method invoked when your app is relaunched into
 *						the background to complete some Bluetooth-related task. Use this method to synchronize your app's state with the state of the
 *						Bluetooth system.
 *
 *  @seealso            CBCentralManagerRestoredStatePeripheralsKey;
 *  @seealso            CBCentralManagerRestoredStateScanServicesKey;
 *  @seealso            CBCentralManagerRestoredStateScanOptionsKey;
 *
 */
- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *, id> *)dict{
    
}

/*!
 *  @method centralManager:didDiscoverPeripheral:advertisementData:RSSI:
 *
 *  @param central              The central manager providing this update.
 *  @param peripheral           A <code>CBPeripheral</code> object.
 *  @param advertisementData    A dictionary containing any advertisement and scan response data.
 *  @param RSSI                 The current RSSI of <i>peripheral</i>, in dBm. A value of <code>127</code> is reserved and indicates the RSSI
 *								was not available.
 *
 *  @discussion                 This method is invoked while scanning, upon the discovery of <i>peripheral</i> by <i>central</i>. A discovered peripheral must
 *                              be retained in order to use it; otherwise, it is assumed to not be of interest and will be cleaned up by the central manager. For
 *                              a list of <i>advertisementData</i> keys, see {@link CBAdvertisementDataLocalNameKey} and other similar constants.
 *
 *  @seealso                    CBAdvertisementData.h
 *
 */
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI{
   
    CBPeripheral *yhPeripheral = (CBPeripheral*)peripheral;
    
    yhPeripheral.scanfTime = [NSDate date];
    
    if ([self.peripherals containsObject:peripheral]) {
        [self.peripherals replaceObjectAtIndex:[self.peripherals indexOfObject:peripheral] withObject:yhPeripheral];
    }else{
        [self.peripherals addObject:peripheral];
    }
    
    
    
    NSMutableArray *removeArr = [[NSMutableArray alloc]init];
    for (CBPeripheral *perItem in self.peripherals) {
        double intervalTime = [[NSDate date] timeIntervalSinceReferenceDate] - [perItem.scanfTime timeIntervalSinceReferenceDate];
        if (intervalTime > 8) {
            [removeArr addObject:perItem];
        }
    }
    [self.peripherals removeObjectsInArray:removeArr];
    
    [[NSNotificationCenter defaultCenter] postNotificationName:YH_BLE_CBPeripheral_found object:nil];
    
}

/*!
 *  @method centralManager:didConnectPeripheral:
 *
 *  @param central      The central manager providing this information.
 *  @param peripheral   The <code>CBPeripheral</code> that has connected.
 *
 *  @discussion         This method is invoked when a connection initiated by {@link connectPeripheral:options:} has succeeded.
 *
 */
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
    self.currentConnectedPeripheral = peripheral;
    peripheral.delegate = self;
    [peripheral discoverServices:nil];
}

/*!
 *  @method centralManager:didFailToConnectPeripheral:error:
 *
 *  @param central      The central manager providing this information.
 *  @param peripheral   The <code>CBPeripheral</code> that has failed to connect.
 *  @param error        The cause of the failure.
 *
 *  @discussion         This method is invoked when a connection initiated by {@link connectPeripheral:options:} has failed to complete. As connection attempts do not
 *                      timeout, the failure of a connection is atypical and usually indicative of a transient issue.
 *
 */
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
    
}

/*!
 *  @method centralManager:didDisconnectPeripheral:error:
 *
 *  @param central      The central manager providing this information.
 *  @param peripheral   The <code>CBPeripheral</code> that has disconnected.
 *  @param error        If an error occurred, the cause of the failure.
 *
 *  @discussion         This method is invoked upon the disconnection of a peripheral that was connected by {@link connectPeripheral:options:}. If the disconnection
 *                      was not initiated by {@link cancelPeripheralConnection}, the cause will be detailed in the <i>error</i> parameter. Once this method has been
 *                      called, no more methods will be invoked on <i>peripheral</i>'s <code>CBPeripheralDelegate</code>.
 *
 */
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error{
    
}




#pragma mark - CBCentralManagerDelegate
/*!
 *  @method peripheralDidUpdateName:
 *
 *  @param peripheral	The peripheral providing this update.
 *
 *  @discussion			This method is invoked when the @link name @/link of <i>peripheral</i> changes.
 */
- (void)peripheralDidUpdateName:(CBPeripheral *)peripheral NS_AVAILABLE(NA, 6_0){
    
}

/*!
 *  @method peripheral:didModifyServices:
 *
 *  @param peripheral			The peripheral providing this update.
 *  @param invalidatedServices	The services that have been invalidated
 *
 *  @discussion			This method is invoked when the @link services @/link of <i>peripheral</i> have been changed.
 *						At this point, the designated <code>CBService</code> objects have been invalidated.
 *						Services can be re-discovered via @link discoverServices: @/link.
 */
- (void)peripheral:(CBPeripheral *)peripheral didModifyServices:(NSArray<CBService *> *)invalidatedServices NS_AVAILABLE(NA, 7_0){
    
}

/*!
 *  @method peripheralDidUpdateRSSI:error:
 *
 *  @param peripheral	The peripheral providing this update.
 *	@param error		If an error occurred, the cause of the failure.
 *
 *  @discussion			This method returns the result of a @link readRSSI: @/link call.
 *
 *  @deprecated			Use {@link peripheral:didReadRSSI:error:} instead.
 */
- (void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(nullable NSError *)error NS_DEPRECATED(NA, NA, 5_0, 8_0){
    
}

/*!
 *  @method peripheral:didReadRSSI:error:
 *
 *  @param peripheral	The peripheral providing this update.
 *  @param RSSI			The current RSSI of the link.
 *  @param error		If an error occurred, the cause of the failure.
 *
 *  @discussion			This method returns the result of a @link readRSSI: @/link call.
 */
- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error NS_AVAILABLE(NA, 8_0){
    
}

/*!
 *  @method peripheral:didDiscoverServices:
 *
 *  @param peripheral	The peripheral providing this information.
 *	@param error		If an error occurred, the cause of the failure.
 *
 *  @discussion			This method returns the result of a @link discoverServices: @/link call. If the service(s) were read successfully, they can be retrieved via
 *						<i>peripheral</i>'s @link services @/link property.
 *
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error{
    
    for (CBService *sevice in peripheral.services) {
        
        if ([sevice.UUID.UUIDString isEqualToString:kServiceUUID]) {
            NSLog(@"搜索到的服务 -- %@",sevice);
            [peripheral discoverCharacteristics:nil forService:sevice];
        }
        
    }
//
}

/*!
 *  @method peripheral:didDiscoverIncludedServicesForService:error:
 *
 *  @param peripheral	The peripheral providing this information.
 *  @param service		The <code>CBService</code> object containing the included services.
 *	@param error		If an error occurred, the cause of the failure.
 *
 *  @discussion			This method returns the result of a @link discoverIncludedServices:forService: @/link call. If the included service(s) were read successfully,
 *						they can be retrieved via <i>service</i>'s <code>includedServices</code> property.
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverIncludedServicesForService:(CBService *)service error:(nullable NSError *)error{
    
}

/*!
 *  @method peripheral:didDiscoverCharacteristicsForService:error:
 *
 *  @param peripheral	The peripheral providing this information.
 *  @param service		The <code>CBService</code> object containing the characteristic(s).
 *	@param error		If an error occurred, the cause of the failure.
 *
 *  @discussion			This method returns the result of a @link discoverCharacteristics:forService: @/link call. If the characteristic(s) were read successfully,
 *						they can be retrieved via <i>service</i>'s <code>characteristics</code> property.
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error{
    if ([service.UUID.UUIDString isEqualToString:kServiceUUID]) {
        
        for (CBCharacteristic *characteristic in service.characteristics) {
            
            if ([characteristic.UUID.UUIDString isEqualToString:kCharacteristicREADUUID]) {
                [self.currentConnectedPeripheral setNotifyValue:YES forCharacteristic:characteristic];
            }
          
        }
    }
}

/*!
 *  @method peripheral:didUpdateValueForCharacteristic:error:
 *
 *  @param peripheral		The peripheral providing this information.
 *  @param characteristic	A <code>CBCharacteristic</code> object.
 *	@param error			If an error occurred, the cause of the failure.
 *
 *  @discussion				This method is invoked after a @link readValueForCharacteristic: @/link call, or upon receipt of a notification/indication.
 */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error{
    [self.reciveBuffer appendData:characteristic.value];
    [[NSNotificationCenter defaultCenter] postNotificationName:YH_BLE_MESSAGE_COMMING object:nil];
    
}

/*!
 *  @method peripheral:didWriteValueForCharacteristic:error:
 *
 *  @param peripheral		The peripheral providing this information.
 *  @param characteristic	A <code>CBCharacteristic</code> object.
 *	@param error			If an error occurred, the cause of the failure.
 *
 *  @discussion				This method returns the result of a {@link writeValue:forCharacteristic:type:} call, when the <code>CBCharacteristicWriteWithResponse</code> type is used.
 */
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error{
    
}

/*!
 *  @method peripheral:didUpdateNotificationStateForCharacteristic:error:
 *
 *  @param peripheral		The peripheral providing this information.
 *  @param characteristic	A <code>CBCharacteristic</code> object.
 *	@param error			If an error occurred, the cause of the failure.
 *
 *  @discussion				This method returns the result of a @link setNotifyValue:forCharacteristic: @/link call.
 */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error{
    
}

/*!
 *  @method peripheral:didDiscoverDescriptorsForCharacteristic:error:
 *
 *  @param peripheral		The peripheral providing this information.
 *  @param characteristic	A <code>CBCharacteristic</code> object.
 *	@param error			If an error occurred, the cause of the failure.
 *
 *  @discussion				This method returns the result of a @link discoverDescriptorsForCharacteristic: @/link call. If the descriptors were read successfully,
 *							they can be retrieved via <i>characteristic</i>'s <code>descriptors</code> property.
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error{
    
}

/*!
 *  @method peripheral:didUpdateValueForDescriptor:error:
 *
 *  @param peripheral		The peripheral providing this information.
 *  @param descriptor		A <code>CBDescriptor</code> object.
 *	@param error			If an error occurred, the cause of the failure.
 *
 *  @discussion				This method returns the result of a @link readValueForDescriptor: @/link call.
 */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error{
    
}

/*!
 *  @method peripheral:didWriteValueForDescriptor:error:
 *
 *  @param peripheral		The peripheral providing this information.
 *  @param descriptor		A <code>CBDescriptor</code> object.
 *	@param error			If an error occurred, the cause of the failure.
 *
 *  @discussion				This method returns the result of a @link writeValue:forDescriptor: @/link call.
 */
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error{
    
}


@end
